/*
 * Copyright (c) 2015 Infosys Ltd. All rights reserved.
 */
package com.infosys.cabplanning.service.plan;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.geocoder.AdvancedGeoCoder;
import com.google.code.geocoder.Geocoder;
import com.google.code.geocoder.GeocoderRequestBuilder;
import com.google.code.geocoder.model.GeocodeResponse;
import com.google.code.geocoder.model.GeocoderRequest;
import com.google.code.geocoder.model.GeocoderResult;

import com.infosys.cabplanning.common.domain.GeoCoordidnate;
import com.infosys.cabplanning.common.util.GenericConstants;

/**
 * The Class MyGeoCoder.
 *
 * @author Anand_Sivadas01
 * @date Feb 28, 2015
 */
public class LocationFinder {
	
	public static Logger LOGGER = LoggerFactory.getLogger(LocationFinder.class);
	private static String TRINFY_SEZ = "Infosys Phase II, Kulathoor, Thiruvananthapuram, Kerala 695583, India"; // "lat" : 8.533265999999999, "lng" : 76.88353599999999

	/**
	 * The main method.
	 *
	 * @param args the arguments
	 */
	public static void main(String[] args) {
//		testMyGeoCoder();
		getCoordinatesForAllLocations();
	}
	
	/**
	 * Test my geo coder.
	 */
	@Test
	public void testMyGeoCoder() {
		try {
			final Geocoder geocoder = new Geocoder();
			GeocoderRequest geocoderRequest = new GeocoderRequestBuilder().setAddress("New York, USA").setLanguage("en").getGeocoderRequest();
				GeocodeResponse geocoderResponse = geocoder.geocode(geocoderRequest);
				List<GeocoderResult> results = geocoderResponse.getResults();
				for (GeocoderResult geocoderResult : results) {
					BigDecimal lat = geocoderResult.getGeometry().getLocation().getLat();
					BigDecimal lng = geocoderResult.getGeometry().getLocation().getLng();
					LOGGER.info(lat + ", " + lng);
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage());
			}
		
	}
	
	/**
	 * Gets the coordinates.
	 *
	 * @param address the address
	 * @return the coordinates
	 */
	private static GeoCoordidnate getCoordinates(String address) {
		//TODO create singleton for GeoCoder
		
		HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
		//set proxy details
//		httpClient.getHostConfiguration().setProxy("10.145.117.132", 80);
		//set timeout 60 seconds
		httpClient.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60 * 1000);



		GeoCoordidnate geoCodidnates = new GeoCoordidnate();
		try {
//		final Geocoder geocoder = new Geocoder();
			final Geocoder geocoder = new AdvancedGeoCoder(httpClient);
			GeocoderRequest geocoderRequest = new GeocoderRequestBuilder().setAddress(address).setLanguage("en").getGeocoderRequest();
			GeocodeResponse geocoderResponse = geocoder.geocode(geocoderRequest);
			List<GeocoderResult> results = geocoderResponse.getResults();
			for (GeocoderResult geocoderResult : results) {
				//TODO handle null cases 
				geoCodidnates.setAddress(geocoderResult.getFormattedAddress());
				geoCodidnates.setLatitude(geocoderResult.getGeometry().getLocation().getLat());
				geoCodidnates.setLongitude(geocoderResult.getGeometry().getLocation().getLng());
			}
		} catch (IOException e) {
			LOGGER.error(e.getMessage());
		}
		
		return geoCodidnates;
	}
	
	/**
	 * Test my logic.
	 */
	
	@Test
	public static void testtMyLogic(){
		List<GeoCoordidnate> coordinatesForAddresses = getCoordinatesForAddresses(getAddresses());
		for (GeoCoordidnate geoCodidnates : coordinatesForAddresses) {
			LOGGER.info(geoCodidnates.toString());
		}
		LocationSorter locationSorter = new LocationSorter(coordinatesForAddresses, GenericConstants.LONGITUDE, GenericConstants.SORT_ASCENDING);
		List<GeoCoordidnate> sortLocations = locationSorter.sortLocations();
		for (GeoCoordidnate geoCodidnates : sortLocations) {
			LOGGER.info(geoCodidnates.toString());
		}
		locationSorter.setSortOrder(GenericConstants.SORT_DESCENDING);
		sortLocations = locationSorter.sortLocations();
		for (GeoCoordidnate geoCodidnates : sortLocations) {
			LOGGER.info(geoCodidnates.toString());
		}
		
	}
	
	public static Map<String, GeoCoordidnate> getCoordinateMapForAllLocations() {
		
		Map<String, GeoCoordidnate> geoCoordinateMap = new HashMap<String, GeoCoordidnate>();
		List<GeoCoordidnate> coordinatesForAllLocations = getCoordinatesForAllLocations();
		for (GeoCoordidnate geoCoordidnate : coordinatesForAllLocations) {
			geoCoordinateMap.put(geoCoordidnate.getAddress(), geoCoordidnate);
		}
		
		return geoCoordinateMap;
	}
	
	public static List<GeoCoordidnate> getCoordinatesForAllLocations(){
		List<GeoCoordidnate> coordinatesForAddresses = getCoordinatesForAddresses(getAddresses());
		LocationSorter locationSorter = new LocationSorter(coordinatesForAddresses, GenericConstants.LONGITUDE, GenericConstants.SORT_ASCENDING);
		List<GeoCoordidnate> sortLocations = locationSorter.sortLocations();
		for (GeoCoordidnate geoCodidnates : sortLocations) {
			LOGGER.info(geoCodidnates.toString());
		}
		
		return sortLocations;
	}
	
	public static GeoCoordidnate getCoordinatesForBaseLocation(String baseLocation) {
		
		 GeoCoordidnate basecoordinate = getCoordinates(baseLocation);
		 return basecoordinate;
	}
	
	/**
	 * Gets the coordinates for addresses.
	 *
	 * @param addresses the addresses
	 * @return the coordinates for addresses
	 */
	private static List<GeoCoordidnate> getCoordinatesForAddresses(List<String> addresses) {
		
		List<GeoCoordidnate> geoCordinates = new ArrayList<GeoCoordidnate>();
//		List<String> addresses = getAddresses();
		for (String address : addresses) {
			geoCordinates.add((getCoordinates(address)));
		}
		
		return geoCordinates;
	}
	
	/**
	 * Gets the addresses.
	 *
	 * @return the addresses
	 */
	private static List<String> getAddresses() {
		
		 List<String> addressList = new ArrayList<String>();
		 // add Start point
		 addressList.add(TRINFY_SEZ);
		 //add drop points
		 addressList.addAll(fetchAddressesFromDB());
		 
		 return addressList;
	}
	
	
	/**
	 * Fetch addresses from db.
	 *
	 * @return the list
	 */
	private static List<String> fetchAddressesFromDB() {
		
		String kazhakkoottam = "Kazhakootam Post office, Sreekariyam Kulathoor Road, Kazhakkoottam, Kazhakkottam, Thiruvananthapuram, Kerala 695582, India";//"lat" : 8.5668905,  "lng" : 76.87294679999999
		String sreekaryam = "Sreekariyam, Thiruvananthapuram, Kerala, India"; // "lat" : 8.5488,  "lng" : 76.9173
		String anayara = "Anayara, Thiruvananthapuram, Kerala, India"; //"lat" : 8.5096484,  "lng" : 76.91676889999999
		String pattom = "Pattom, Thiruvananthapuram, Kerala 695004, India"; // "lat" : 8.5207295, "lng" : 76.94228729999999
		String medCollege = "Medical College, Thiruvananthapuram, Kerala, India"; // "lat" : 8.522073199999999, "lng" : 76.9268541
		String kovalam = "Kovalam, Kovalam, Kerala, India"; // "lat" : 8.4003984,  "lng" : 76.97870759999999
		String techPark = "Technopark Campus, Kazhakkoottam, Kerala, India"; // "lat" : 8.558095699999999, "lng" : 76.8807437
		
		List<String> addressList = new ArrayList<String>();
		addressList.add(kazhakkoottam);
		addressList.add(sreekaryam);
		addressList.add(anayara);
		addressList.add(pattom);
		addressList.add(medCollege);
		addressList.add(kovalam);
		addressList.add(techPark);
		
		return addressList;
	}

}
